#include "koops/frame.h"
#include "koops/stacktrace.h"
#include "location.h"
#include "utils.h"
#include "thread.h"
#include "stacktrace.h"
#include <stdio.h>
#include <glib.h>

static void
test_koops_stacktrace_parse_modules(void)
{
    struct test_data
    {
        bool success;
        char *input;
        char **expected_modules;
    };

    struct test_data t[] = {
    };

    for (int i = 0; i < sizeof (t) / sizeof (*t); i++)
    {
        char *old_input = t[i].input;
        char **modules = sr_koops_stacktrace_parse_modules((const char **)&t[i].input);
        g_assert_true(!!modules == t[i].success);

        if (modules)
        {
            int offset = 0;
            while (modules[offset])
            {
                g_assert_nonnull(t[i].expected_modules[offset]);
                g_assert_cmpstr(modules[offset], ==, t[i].expected_modules[offset]);
                ++offset;
            }
            g_assert_null(t[i].expected_modules[offset]);

            offset = 0;
            while (modules[offset])
            {
                g_free(modules[offset]);
                ++offset;
            }

            g_free(modules);
        }
        else
        {
            /* Check that the pointer is not moved. */
            g_assert_true(old_input == t[i].input);
        }
    }
}

static void
test_koops_stacktrace_parse(void)
{
    struct test_data
    {
        const char *path;
        const char *first_function_name;
        const char *last_function_name;
        int frame_count;
        int taint_count;
        int module_count;
        char *first_frame_stack;
        char *last_frame_stack;
    };

    struct test_data t[] = {
        {"kerneloopses/gitlog-01", "unwind_backtrace", "kthread", 15, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-02", "unwind_backtrace", "cpu_idle", 27, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-03", "lockdep_rcu_suspicious", "system_call_fastpath", 19, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-04", "lockdep_rcu_suspicious", "addrconf_forward_change", 7, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-05", "mark_held_locks", "sysenter_do_call", 26, 1, 0, NULL, NULL},
        {"kerneloopses/gitlog-06", "lock_acquire", "kernel_thread_helper", 23, 1, 0, NULL, NULL},
        {"kerneloopses/gitlog-07", "__slab_alloc.isra.50.constprop.56", "sysenter_do_call", 14, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-08", "unwind_backtrace", "sys_write", 13, 1, 0, NULL, NULL},
        {"kerneloopses/gitlog-09", "unwind_backtrace", "cpu_idle", 11, 1, 0, NULL, NULL},
        {"kerneloopses/gitlog-10", NULL, NULL, 0, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-11", "lockdep_rcu_suspicious", "out_of_memory", 12, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-12", "unwind_backtrace", "regulator_get_voltage", 6, 1, 0, NULL, NULL},

        /*
        {"kerneloopses/gitlog-13", "", "", 0, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-14", "", "", 0, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-15", "", "", 0, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-16", "", "", 0, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-17", "", "", 0, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-18", "", "", 0, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-19", "", "", 0, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-20", "", "", 0, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-21", "", "", 0, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-22", "", "", 0, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-23", "", "", 0, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-24", "", "", 0, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-25", "", "", 0, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-26", "", "", 0, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-27", "", "", 0, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-28", "", "", 0, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-29", "", "", 0, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-30", "", "", 0, 0, 0, NULL, NULL},
        {"kerneloopses/gitlog-31", "", "", 0, 0, 0, NULL, NULL},
        */

        {"kerneloopses/rhbz-827868", "warn_slowpath_common", "gs_change", 32, 0, 119, NULL, NULL},
        {"kerneloopses/rhbz-836206", "__might_sleep", "system_call_fastpath", 23, 0, 59, NULL, NULL},
        {"kerneloopses/rhbz-865695-0", "wdev_priv.part.8", "sysenter_do_call", 25, 3, 56, NULL, NULL},
        {"kerneloopses/rhbz-865695-2", "wdev_priv.part.8", "system_call_fastpath", 20, 2, 103, NULL, NULL},
        {"kerneloopses/rhbz-865695-2-notime", "wdev_priv.part.8", "system_call_fastpath", 20, 2, 103, NULL, NULL},
        {"kerneloopses/rhbz-1040900-s390x-1", "will_oops_init", NULL, 6, 2, 24, NULL, NULL},
        {"kerneloopses/rhbz-1040900-s390x-2", NULL, NULL, 17, 0, 2, NULL, NULL},
        {"kerneloopses/rhbz-1040900-ppc64-1", "eeh_dev_check_failure", "syscall_exit", 20, 0, 13, NULL, NULL},
        {"kerneloopses/github-102", "dump_stack", "kthread_create_on_node", 17, 6, 104, NULL, NULL},
        {"kerneloopses/github-73", "dump_stack", "common_interrupt", 33, 0, 100, "IRQ", "IRQ"},
        {"kerneloopses/github-73-modified", "dump_stack", "common_interrupt", 33, 0, 100, "NMI", NULL},
        {"kerneloopses/rhbz-1235021", "smp_call_function_many", "system_call_fastpath", 29, 0, 110, NULL, NULL},
        {"kerneloopses/arm-hung-task-oops", "dump_backtrace_log_lvl", "kthread", 6, 2, 0, NULL, NULL},
        {"kerneloopses/arm-hung-task-oops-2", "shmem_getpage_gfp", "SyS_write", 7, 0, 53, NULL, NULL},
        {"kerneloopses/rhbz-1518943", "amdgpu_vm_sdma_update", "entry_SYSCALL_64_after_hwframe", 17, 0, 87, NULL, NULL},
    };

    for (int i = 0; i < sizeof (t) / sizeof (*t); i++)
    {
        char *error_message;
        char *full_input = sr_file_to_string(t[i].path, &error_message);
        g_assert_nonnull(full_input);
        char *input = full_input;

        struct sr_location location;
        sr_location_init(&location);
        struct sr_koops_stacktrace *stacktrace =
            sr_koops_stacktrace_parse((const char **)&input, &location);

        g_assert_nonnull(stacktrace);
        g_assert_cmpint(*input, ==, '\0');

        if (stacktrace->frames)
        {
            g_assert_cmpstr(stacktrace->frames->function_name, ==, t[i].first_function_name);
            g_assert_cmpstr(stacktrace->frames->special_stack, ==, t[i].first_frame_stack);

            struct sr_koops_frame *last = stacktrace->frames;
            while (last->next)
                last = last->next;

            g_assert_cmpstr(last->function_name, ==, t[i].last_function_name);
            g_assert_cmpstr(last->special_stack, ==, t[i].last_frame_stack);
        }
        else
            g_assert_true(!t[i].first_function_name && !t[i].last_function_name && 0 == t[i].frame_count);

        printf("Frames expected: %d, got: %d\n", t[i].frame_count,
               sr_thread_frame_count((struct sr_thread*) stacktrace));
        g_assert_true(t[i].frame_count == sr_thread_frame_count((struct sr_thread*) stacktrace));

        char *reason = sr_koops_stacktrace_get_reason(stacktrace);
        g_assert_nonnull(reason);
        char *reason2 = sr_stacktrace_get_reason((struct sr_stacktrace *)stacktrace);
        g_assert_nonnull(reason2);
        g_assert_cmpstr(reason, ==, reason2);
        g_free(reason);
        g_free(reason2);

        int actual_taint_count =
          stacktrace->taint_module_proprietary +
          stacktrace->taint_module_out_of_tree +
          stacktrace->taint_forced_module +
          stacktrace->taint_forced_removal +
          stacktrace->taint_smp_unsafe +
          stacktrace->taint_mce +
          stacktrace->taint_page_release +
          stacktrace->taint_userspace +
          stacktrace->taint_died_recently +
          stacktrace->taint_acpi_overridden +
          stacktrace->taint_warning +
          stacktrace->taint_staging_driver +
          stacktrace->taint_firmware_workaround +
          stacktrace->taint_unsigned_module +
          stacktrace->taint_soft_lockup +
          stacktrace->taint_live_patched
          ;

        g_assert_true(actual_taint_count == t[i].taint_count);

        int actual_module_count = 0;
        char** mod = stacktrace->modules;
        while (mod && *mod)
        {
          mod++;
          actual_module_count++;
        }
        printf("Modules expected: %d, got: %d\n", t[i].module_count, actual_module_count);
        g_assert_true(actual_module_count == t[i].module_count);

        /* test parsing via sr_stacktrace_parse */
        input = full_input;
        char *error;
        struct sr_stacktrace *stacktrace2 = sr_stacktrace_parse(SR_REPORT_KERNELOOPS, input, &error);

        g_assert_nonnull(stacktrace2);
        reason = sr_stacktrace_get_reason((struct sr_stacktrace *)stacktrace);
        reason2 = sr_stacktrace_get_reason(stacktrace2);

        g_assert_true(reason && reason2);
        g_assert_cmpstr(reason, ==, reason2);

        g_free(reason);
        g_free(reason2);

        sr_stacktrace_free(stacktrace2);

        sr_koops_stacktrace_free(stacktrace);
        g_free(full_input);
    }
}

static void
test_koops_stacktrace_to_json(void)
{
    char *error_message;
    char *full_input = sr_file_to_string("kerneloopses/rhbz-827868-modified",
                                          &error_message);

    g_assert_nonnull(full_input);
    char *input = full_input;

    struct sr_location location;
    sr_location_init(&location);
    struct sr_koops_stacktrace *stacktrace =
        sr_koops_stacktrace_parse((const char **)&input, &location);

    g_assert_nonnull(stacktrace);
    g_assert_cmpint(*input, ==, '\0');
    g_free(full_input);

    char *json = sr_koops_stacktrace_to_json(stacktrace);
    puts(json);
    g_assert_cmpstr(json, ==,
        "{   \"raw_oops\": \"[110417.280426] WARNING: at mm/page_alloc.c:2204 __alloc_pages_nodemask+0x231/0x8f0()\\n"
        "[110417.280429] Hardware name: Latitude E6420\\n"
        "[110417.280431] Modules linked in: vfat fat usb_storage authenc l2tp_ppp pppox ppp_generic slhc l2tp_netlink "
        "l2tp_core rmd160 crypto_null camellia lzo cast6 cast5 deflate zlib_deflate cts gcm ccm serpent_sse2_x86_64 "
        "serpent_generic blowfish_generic blowfish_x86_64 blowfish_common twofish_generic twofish_x86_64_3way lrw "
        "twofish_x86_64 twofish_common xcbc sha256_generic sha512_generic des_generic ah6 ah4 esp6 esp4 "
        "xfrm4_mode_beet xfrm4_tunnel tunnel4 xfrm4_mode_tunnel xfrm4_mode_transport xfrm6_mode_transport "
        "xfrm6_mode_ro xfrm6_mode_beet xfrm6_mode_tunnel ipcomp ipcomp6 xfrm_ipcomp xfrm6_tunnel tunnel6 af_key "
        "snd_usb_audio snd_usbmidi_lib snd_rawmidi sctp libcrc32c tcp_lp wacom fuse lockd rfcomm bnep "
        "snd_hda_codec_hdmi snd_hda_codec_idt binfmt_misc arc4 uvcvideo snd_hda_intel videobuf2_core videodev "
        "snd_hda_codec ppdev media dell_wmi videobuf2_vmalloc sparse_keymap videobuf2_memops dell_laptop snd_hwdep "
        "snd_seq uinput dcdbas snd_seq_device btusb bluetooth iwlwifi snd_pcm parport_pc \\n"
        "joydev parport snd_timer microcode mac80211 snd soundcore snd_page_alloc i2c_i801 cfg80211 iTCO_wdt "
        "iTCO_vendor_support rfkill e1000e sunrpc xts gf128mul dm_crypt sdhci_pci sdhci mmc_core wmi i915 "
        "drm_kms_helper drm i2c_algo_bit i2c_core video [last unloaded: scsi_wait_scan]\\n"
        "[110417.280525] Pid: 3, comm: ksoftirqd/0 Tainted: P    B D    O 3.3.7-1.fc16.x86_64 #1\\n"
        "[110417.280528] Call Trace:\\n"
        "[110417.280535]  [<ffffffff81057adf>] warn_slowpath_common+0x7f/0xc0\\n"
        "[110417.280539]  [<ffffffff81057b3a>] warn_slowpath_null+0x1a/0x20\\n"
        "[110417.280544]  [<ffffffff811296d1>] __alloc_pages_nodemask+0x231/0x8f0\\n"
        "[110417.280550]  [<ffffffff8151fd80>] ? ip_copy_metadata+0x1c0/0x1c0\\n"
        "[110417.280555]  [<ffffffff8151f860>] ? ip_forward_options+0x1f0/0x1f0\\n"
        "[110417.280559]  [<ffffffff81160a93>] alloc_pages_current+0xa3/0x110\\n"
        "[110417.280563]  [<ffffffff81125514>] __get_free_pages+0x14/0x50\\n"
        "[110417.280569]  [<ffffffff8116bb5f>] kmalloc_order_trace+0x3f/0xd0\\n"
        "[110417.280573]  [<ffffffff8116ca87>] __kmalloc+0x177/0x1a0\\n"
        "[110417.280578]  [<ffffffffa0642332>] ? pppol2tp_xmit+0x42/0x220 [l2tp_ppp]\\n"
        "[110417.280583]  [<ffffffff814db0a7>] pskb_expand_head+0x87/0x310\\n"
        "[110417.280588]  [<ffffffff8113dc59>] ? __mod_zone_page_state+0x49/0x50\\n"
        "[110417.280591]  [<ffffffff814da0f5>] ? kfree_skb+0x45/0xc0\\n"
        "[110417.280595]  [<ffffffffa06424dd>] pppol2tp_xmit+0x1ed/0x220 [l2tp_ppp]\\n"
        "[110417.280600]  [<ffffffffa062dd5b>] ppp_push+0x15b/0x650 [ppp_generic]\\n"
        "[110417.280604]  [<ffffffff814db264>] ? pskb_expand_head+0x244/0x310\\n"
        "[110417.280608]  [<ffffffff8112860b>] ? free_compound_page+0x1b/0x20\\n"
        "[110417.280612]  [<ffffffff8112d053>] ? __put_compound_page+0x23/0x30\\n"
        "[110417.280615]  [<ffffffff8112d1d5>] ? put_compound_page+0x125/0x1c0\\n"
        "[110417.280619]  [<ffffffffa062e89f>] ppp_xmit_process+0x46f/0x660 [ppp_generic]\\n"
        "[110417.280624]  [<ffffffffa062ebc8>] ppp_start_xmit+0x138/0x1d0 [ppp_generic]\\n"
        "[110417.280628]  [<ffffffff814e85b2>] dev_hard_start_xmit+0x332/0x6d0\\n"
        "[110417.280632]  [<ffffffff8150447a>] sch_direct_xmit+0xfa/0x1d0\\n"
        "[110417.280635]  [<ffffffff815045f6>] __qdisc_run+0xa6/0x130\\n"
        "[110417.280639]  [<ffffffff814e6883>] net_tx_action+0xe3/0x1f0\\n"
        "[110417.280643]  [<ffffffff8105f0c8>] __do_softirq+0xb8/0x230\\n"
        "[110417.280646]  [<ffffffff8105f2fa>] run_ksoftirqd+0xba/0x170\\n"
        "[110417.280649]  [<ffffffff8105f240>] ? __do_softirq+0x230/0x230\\n"
        "[110417.280654]  [<ffffffff81079da3>] kthread+0x93/0xa0\\n"
        "[110417.280658]  [<ffffffff815fd8e4>] kernel_thread_helper+0x4/0x10\\n"
        "[110417.280663]  [<ffffffff81079d10>] ? kthread_freezable_should_stop+0x70/0x70\\n"
        "[110417.280666]  [<ffffffff815fd8e0>] ? gs_change+0x13/0x13\\n\"\n"
        ",   \"taint_flags\": [ \"module_proprietary\"\n"
        "                   , \"page_release\"\n"
        "                   , \"died_recently\"\n"
        "                   , \"module_out_of_tree\" ]\n"
        ",   \"modules\":\n"
        "      [ \"vfat\"\n"
        "      , \"fat\"\n"
        "      , \"usb_storage\"\n"
        "      , \"authenc\"\n"
        "      , \"l2tp_ppp\"\n"
        "      , \"pppox\"\n"
        "      , \"ppp_generic\"\n"
        "      , \"slhc\"\n"
        "      , \"l2tp_netlink\"\n"
        "      , \"l2tp_core\"\n"
        "      , \"rmd160\"\n"
        "      , \"crypto_null\"\n"
        "      , \"camellia\"\n"
        "      , \"lzo\"\n"
        "      , \"cast6\"\n"
        "      , \"cast5\"\n"
        "      , \"deflate\"\n"
        "      , \"zlib_deflate\"\n"
        "      , \"cts\"\n"
        "      , \"gcm\"\n"
        "      , \"ccm\"\n"
        "      , \"serpent_sse2_x86_64\"\n"
        "      , \"serpent_generic\"\n"
        "      , \"blowfish_generic\"\n"
        "      , \"blowfish_x86_64\"\n"
        "      , \"blowfish_common\"\n"
        "      , \"twofish_generic\"\n"
        "      , \"twofish_x86_64_3way\"\n"
        "      , \"lrw\"\n"
        "      , \"twofish_x86_64\"\n"
        "      , \"twofish_common\"\n"
        "      , \"xcbc\"\n"
        "      , \"sha256_generic\"\n"
        "      , \"sha512_generic\"\n"
        "      , \"des_generic\"\n"
        "      , \"ah6\"\n"
        "      , \"ah4\"\n"
        "      , \"esp6\"\n"
        "      , \"esp4\"\n"
        "      , \"xfrm4_mode_beet\"\n"
        "      , \"xfrm4_tunnel\"\n"
        "      , \"tunnel4\"\n"
        "      , \"xfrm4_mode_tunnel\"\n"
        "      , \"xfrm4_mode_transport\"\n"
        "      , \"xfrm6_mode_transport\"\n"
        "      , \"xfrm6_mode_ro\"\n"
        "      , \"xfrm6_mode_beet\"\n"
        "      , \"xfrm6_mode_tunnel\"\n"
        "      , \"ipcomp\"\n"
        "      , \"ipcomp6\"\n"
        "      , \"xfrm_ipcomp\"\n"
        "      , \"xfrm6_tunnel\"\n"
        "      , \"tunnel6\"\n"
        "      , \"af_key\"\n"
        "      , \"snd_usb_audio\"\n"
        "      , \"snd_usbmidi_lib\"\n"
        "      , \"snd_rawmidi\"\n"
        "      , \"sctp\"\n"
        "      , \"libcrc32c\"\n"
        "      , \"tcp_lp\"\n"
        "      , \"wacom\"\n"
        "      , \"fuse\"\n"
        "      , \"lockd\"\n"
        "      , \"rfcomm\"\n"
        "      , \"bnep\"\n"
        "      , \"snd_hda_codec_hdmi\"\n"
        "      , \"snd_hda_codec_idt\"\n"
        "      , \"binfmt_misc\"\n"
        "      , \"arc4\"\n"
        "      , \"uvcvideo\"\n"
        "      , \"snd_hda_intel\"\n"
        "      , \"videobuf2_core\"\n"
        "      , \"videodev\"\n"
        "      , \"snd_hda_codec\"\n"
        "      , \"ppdev\"\n"
        "      , \"media\"\n"
        "      , \"dell_wmi\"\n"
        "      , \"videobuf2_vmalloc\"\n"
        "      , \"sparse_keymap\"\n"
        "      , \"videobuf2_memops\"\n"
        "      , \"dell_laptop\"\n"
        "      , \"snd_hwdep\"\n"
        "      , \"snd_seq\"\n"
        "      , \"uinput\"\n"
        "      , \"dcdbas\"\n"
        "      , \"snd_seq_device\"\n"
        "      , \"btusb\"\n"
        "      , \"bluetooth\"\n"
        "      , \"iwlwifi\"\n"
        "      , \"snd_pcm\"\n"
        "      , \"parport_pc\"\n"
        "      , \"joydev\"\n"
        "      , \"parport\"\n"
        "      , \"snd_timer\"\n"
        "      , \"microcode\"\n"
        "      , \"mac80211\"\n"
        "      , \"snd\"\n"
        "      , \"soundcore\"\n"
        "      , \"snd_page_alloc\"\n"
        "      , \"i2c_i801\"\n"
        "      , \"cfg80211\"\n"
        "      , \"iTCO_wdt\"\n"
        "      , \"iTCO_vendor_support\"\n"
        "      , \"rfkill\"\n"
        "      , \"e1000e\"\n"
        "      , \"sunrpc\"\n"
        "      , \"xts\"\n"
        "      , \"gf128mul\"\n"
        "      , \"dm_crypt\"\n"
        "      , \"sdhci_pci\"\n"
        "      , \"sdhci\"\n"
        "      , \"mmc_core\"\n"
        "      , \"wmi\"\n"
        "      , \"i915\"\n"
        "      , \"drm_kms_helper\"\n"
        "      , \"drm\"\n"
        "      , \"i2c_algo_bit\"\n"
        "      , \"i2c_core\"\n"
        "      , \"video\" ]\n"
        ",   \"frames\":\n"
        "      [ {   \"address\": 18446744071579204319\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"warn_slowpath_common\"\n"
        "        ,   \"function_offset\": 127\n"
        "        ,   \"function_length\": 192\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071579204410\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"warn_slowpath_null\"\n"
        "        ,   \"function_offset\": 26\n"
        "        ,   \"function_length\": 32\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071580063441\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"__alloc_pages_nodemask\"\n"
        "        ,   \"function_offset\": 561\n"
        "        ,   \"function_length\": 2288\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071584218496\n"
        "        ,   \"reliable\": false\n"
        "        ,   \"function_name\": \"ip_copy_metadata\"\n"
        "        ,   \"function_offset\": 448\n"
        "        ,   \"function_length\": 448\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071584217184\n"
        "        ,   \"reliable\": false\n"
        "        ,   \"function_name\": \"ip_forward_options\"\n"
        "        ,   \"function_offset\": 496\n"
        "        ,   \"function_length\": 496\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071580289683\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"alloc_pages_current\"\n"
        "        ,   \"function_offset\": 163\n"
        "        ,   \"function_length\": 272\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071580046612\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"__get_free_pages\"\n"
        "        ,   \"function_offset\": 20\n"
        "        ,   \"function_length\": 80\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071580334943\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"kmalloc_order_trace\"\n"
        "        ,   \"function_offset\": 63\n"
        "        ,   \"function_length\": 208\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071580338823\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"__kmalloc\"\n"
        "        ,   \"function_offset\": 375\n"
        "        ,   \"function_length\": 416\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744072105501490\n"
        "        ,   \"reliable\": false\n"
        "        ,   \"function_name\": \"pppol2tp_xmit\"\n"
        "        ,   \"function_offset\": 66\n"
        "        ,   \"function_length\": 544\n"
        "        ,   \"module_name\": \"l2tp_ppp\"\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071583936679\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"pskb_expand_head\"\n"
        "        ,   \"function_offset\": 135\n"
        "        ,   \"function_length\": 784\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071580146777\n"
        "        ,   \"reliable\": false\n"
        "        ,   \"function_name\": \"__mod_zone_page_state\"\n"
        "        ,   \"function_offset\": 73\n"
        "        ,   \"function_length\": 80\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071583932661\n"
        "        ,   \"reliable\": false\n"
        "        ,   \"function_name\": \"kfree_skb\"\n"
        "        ,   \"function_offset\": 69\n"
        "        ,   \"function_length\": 192\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744072105501917\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"pppol2tp_xmit\"\n"
        "        ,   \"function_offset\": 493\n"
        "        ,   \"function_length\": 544\n"
        "        ,   \"module_name\": \"l2tp_ppp\"\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744072105418075\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"ppp_push\"\n"
        "        ,   \"function_offset\": 347\n"
        "        ,   \"function_length\": 1616\n"
        "        ,   \"module_name\": \"ppp_generic\"\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071583937124\n"
        "        ,   \"reliable\": false\n"
        "        ,   \"function_name\": \"pskb_expand_head\"\n"
        "        ,   \"function_offset\": 580\n"
        "        ,   \"function_length\": 784\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071580059147\n"
        "        ,   \"reliable\": false\n"
        "        ,   \"function_name\": \"free_compound_page\"\n"
        "        ,   \"function_offset\": 27\n"
        "        ,   \"function_length\": 32\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071580078163\n"
        "        ,   \"reliable\": false\n"
        "        ,   \"function_name\": \"__put_compound_page\"\n"
        "        ,   \"function_offset\": 35\n"
        "        ,   \"function_length\": 48\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071580078549\n"
        "        ,   \"reliable\": false\n"
        "        ,   \"function_name\": \"put_compound_page\"\n"
        "        ,   \"function_offset\": 293\n"
        "        ,   \"function_length\": 448\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744072105420959\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"ppp_xmit_process\"\n"
        "        ,   \"function_offset\": 1135\n"
        "        ,   \"function_length\": 1632\n"
        "        ,   \"module_name\": \"ppp_generic\"\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744072105421768\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"ppp_start_xmit\"\n"
        "        ,   \"function_offset\": 312\n"
        "        ,   \"function_length\": 464\n"
        "        ,   \"module_name\": \"ppp_generic\"\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071583991218\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"dev_hard_start_xmit\"\n"
        "        ,   \"function_offset\": 818\n"
        "        ,   \"function_length\": 1744\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071584105594\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"sch_direct_xmit\"\n"
        "        ,   \"function_offset\": 250\n"
        "        ,   \"function_length\": 464\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071584105974\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"__qdisc_run\"\n"
        "        ,   \"function_offset\": 166\n"
        "        ,   \"function_length\": 304\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071583983747\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"net_tx_action\"\n"
        "        ,   \"function_offset\": 227\n"
        "        ,   \"function_length\": 496\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071579234504\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"__do_softirq\"\n"
        "        ,   \"function_offset\": 184\n"
        "        ,   \"function_length\": 560\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071579235066\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"run_ksoftirqd\"\n"
        "        ,   \"function_offset\": 186\n"
        "        ,   \"function_length\": 368\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071579234880\n"
        "        ,   \"reliable\": false\n"
        "        ,   \"function_name\": \"__do_softirq\"\n"
        "        ,   \"function_offset\": 560\n"
        "        ,   \"function_length\": 560\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071579344291\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"kthread\"\n"
        "        ,   \"function_offset\": 147\n"
        "        ,   \"function_length\": 160\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071585126628\n"
        "        ,   \"reliable\": true\n"
        "        ,   \"function_name\": \"kernel_thread_helper\"\n"
        "        ,   \"function_offset\": 4\n"
        "        ,   \"function_length\": 16\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071579344144\n"
        "        ,   \"reliable\": false\n"
        "        ,   \"function_name\": \"kthread_freezable_should_stop\"\n"
        "        ,   \"function_offset\": 112\n"
        "        ,   \"function_length\": 112\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        }\n"
        "      , {   \"address\": 18446744071585126624\n"
        "        ,   \"reliable\": false\n"
        "        ,   \"function_name\": \"gs_change\"\n"
        "        ,   \"function_offset\": 19\n"
        "        ,   \"function_length\": 19\n"
        "        ,   \"from_function_offset\": 0\n"
        "        ,   \"from_function_length\": 0\n"
        "        } ]\n"
        "}");

    char *json2 = sr_stacktrace_to_json((struct sr_stacktrace *)stacktrace);
    g_assert_cmpstr(json, ==, json2);
    g_free(json2);

    sr_koops_stacktrace_free(stacktrace);
    g_free(json);
}

void generate_and_test(struct sr_thread *thread, int flags, const char *expected)
{
    char *hash = sr_thread_get_duphash(thread, 1, NULL, flags);

    if (NULL == hash && NULL != expected)
    {
        fprintf(stderr, "'%s' != NULL\n", expected);
        g_assert_false("NULL was NOT expected");
    }

    if (NULL != hash && NULL == expected)
    {
        fprintf(stderr, "NULL != '%s'\n", hash);
        g_assert_false("NULL was expected");
    }

    if (NULL == hash && NULL == expected)
        return;

    if (strcmp(hash, expected) != 0)
    {
        fprintf(stderr, "'%s' != '%s'\n", expected, hash);
    }

    g_free(hash);
}

void test(struct sr_thread *thread, const char *expected, const char *expected_compat)
{
    generate_and_test(thread, SR_DUPHASH_NOHASH, expected);
    fprintf(stderr, "COMPAT\n");
    generate_and_test(thread, SR_DUPHASH_NOHASH|SR_DUPHASH_KOOPS_COMPAT, expected_compat);
}

static void
test_thread_get_duphash(void)
{
    struct sr_koops_stacktrace *stacktrace = sr_koops_stacktrace_new();
    struct sr_thread *thread = (struct sr_thread *)stacktrace;

    struct sr_koops_frame *frame = sr_koops_frame_new();
    stacktrace->frames = frame;

    frame->address = 0xDEADBEAF;

    fprintf(stderr, "Checkpoint 1\n");
    frame->reliable = 1;
    test(thread, "Thread\n0xdeadbeaf\n", "0xdeadbeaf\n");

    fprintf(stderr, "Checkpoint 2\n");
    frame->reliable = 0;
    test(thread, "Thread\n0xdeadbeaf\n", NULL);

    frame->address = 0xDEADBEAF;
    frame->function_name = g_strdup("omg_warn_slowpath_common");
    stacktrace->frames = frame;

    fprintf(stderr, "Checkpoint 3\n");
    frame->reliable = 1;
    test(thread, "Thread\nomg_warn_slowpath_common\n", "omg_warn_slowpath_common\n");

    fprintf(stderr, "Checkpoint 4\n");
    frame->reliable = 0;
    test(thread, "Thread\nomg_warn_slowpath_common\n", NULL);
    sr_koops_stacktrace_free(stacktrace);
}

static void
test_koops_stacktrace_get_reason(void)
{
    char *error_message;
    char *full_input = sr_file_to_string("kerneloopses/rhbz-1140681", &error_message);
    g_assert_nonnull(full_input);
    char *input = full_input;

    struct sr_location location;
    sr_location_init(&location);
    struct sr_koops_stacktrace *stacktrace =
        sr_koops_stacktrace_parse((const char **)&input, &location);

    g_assert_nonnull(stacktrace);
    g_assert_cmpint(*input, ==, '\0');
    g_free(full_input);

    char *expected_reason = "general protection fault in find_get_entry";
    g_autofree char *actual_reason = sr_koops_stacktrace_get_reason(stacktrace);
    printf("%s\n%s\n", expected_reason, actual_reason);
    g_assert_cmpstr(expected_reason, ==, actual_reason);

    sr_koops_stacktrace_free(stacktrace);
}


int
main(int    argc,
     char **argv)
{
    g_test_init(&argc, &argv, NULL);

    g_test_add_func("/stacktrace/koops/parse-modules", test_koops_stacktrace_parse_modules);
    g_test_add_func("/stacktrace/koops/parse", test_koops_stacktrace_parse);
    g_test_add_func("/stacktrace/koops/to-json", test_koops_stacktrace_to_json);
    g_test_add_func("/thread/get-duphash", test_thread_get_duphash);
    g_test_add_func("/stacktrace/koops/get-reason", test_koops_stacktrace_get_reason);

    return g_test_run();
}
